/**
 * fileUploadArea 0.1
 *
 * Copyright (c) 2009 Peter Rudolfsen
 *
 * Licensed under the MIT license:
 *   http://www.opensource.org/licenses/mit-license.php
 *
 * Date: 2009-11-27
 */

/**
 * Add 'sum_numbers' to Array.prototype if it doesn't exist
 * Not called 'sum' to avoid possible crashes with other implementations of 'sum'
 */
Array.prototype.sum_numbers = function () {
  return (!this.length) ? 0 : this.slice(1).sum_numbers() + ((typeof this[0] == 'number') ? this[0] : 0);
};

(function ($) {
  /**
   * Private variables
   */
  
  var options = {},
      totalSize = 0,
      numFiles = 0,
      loadQueue = [],
      completeQueue = [];
  
  /**
   * Private functions
   */

  function dragEnter(e) {
    options.onDragEnter.call(this);
    
    e.stopPropagation();
    e.preventDefault();
  }
  
  function dragOver(e) {
    options.onDragOver.call(this);

    e.stopPropagation();
    e.preventDefault();
  }
  
  function dragLeave(e) {
    options.onDragLeave.call(this);

    e.stopPropagation();
    e.preventDefault();
  }
  
  function drop(e) {
    var files = e.dataTransfer.files,
        i = 0;
    
    numFiles = files.length;
    
    e.stopPropagation();
    e.preventDefault();
    
    /**
     * Call the onDrop callback
     * Calculate total file size
     */
    for (i = 0; i < numFiles; i++) {
      options.onDrop.call(this, files.item(i));
      totalSize += files.item(i).fileSize;
    }
    
    /**
     * Upload each file
     */
    for (i = 0; i < numFiles; i++) {
      uploadFile(files.item(i), i);
    }
  }
  
  function uploadFile(file, queueId) {
    var xhr = new XMLHttpRequest(),
        queryString = '?',
        j = 0;

    xhr.upload.addEventListener('progress', function (e) {
      if (e.lengthComputable) {
        loadQueue[queueId] = e.loaded;
        options.progress.call(file, Math.round((e.loaded * 100) / e.total), Math.round((loadQueue.sum_numbers() / totalSize) * 100));
      }
    }, false);
    
    xhr.upload.addEventListener('load', function (e) {
      loadQueue[queueId] = e.total;
      options.progress.call(file, 100, Math.round((loadQueue.sum_numbers() / totalSize) * 100));
      /**
       * Not in use!
       * Using the AJAX-request to complete the file, to allow for response in the complete callback
       */
    }, false);
    
    /**
     * Sending name and type as GET params in the AJAX-request
     */
    options.params.name = file.fileName;
    options.params.type = file.type;
    
    /**
     * Build the query string based on options.params
     */
    for (j in options.params) {
      if (options.params.hasOwnProperty(j)) {
        queryString += (j + '=' + options.params[j] + '&');
      }
    }
    
    /**
     * Remove trailing '&'
     */
    queryString = queryString.substring(0, queryString.length - 1);
      
    /**
     * Post the file
     */
    xhr.open("POST", options.url + queryString, true);
    xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
    xhr.onreadystatechange = function (event) {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          /**
           * The upload is complete
           */
          completeQueue[queueId] = 1;
          options.fileComplete.call(file, xhr.responseText);
          if (completeQueue.sum_numbers() === numFiles) {
            options.complete.call(null);
          }
        } else {
          throw 'Failed uploading file ' + file.fileName;
        }
      }
    };
    xhr.sendAsBinary(file.getAsBinary());
  }
  
  /**
   * Plugin definition
   */  
  $.fn.fileUploadArea = function (opts) {
    return this.each(function () {
      if (typeof opts !== 'object') {
        opts = {};
      }
      
      options = $.extend({}, {
        url: 'upload.php',
        onDragEnter: function () {},
        onDragOver: function () {},
        onDragLeave: function () {},
        onDrop: function (file) {},
        progress: function (percent, totalPercent) {},
        fileComplete: function () {},
        complete: function () {},
        params: {}
      }, opts);

      this.addEventListener('dragenter', dragEnter, false);
      this.addEventListener('dragover', dragOver, false);
      this.addEventListener('dragleave', dragLeave, false);
      this.addEventListener('drop', drop, false);
    });
  };
})(jQuery);
